package unite;


import java.io.Serializable;

import javax.swing.JOptionPane;

import deroulementjeu.TypeEquipe;

import affichage.FinPartieView;


/**
 * Classe de gestion de la jouabilité
 * @author Alexis Brasy Geoffrey Lequesne Sandrine Piriou Popov Michel
 */
public abstract class Jouable extends Unite implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * Retourne les stats de l'unite
     */
    public void getStat() {}

    /**
     * Taille du champ de vision. Applicable pour les unités jouables.
     * @return Champ de vision.
     */
    @Override
    public int getVision() {
        return (int) ((1 + modulationVisionDep())
                * getRegles().getCaracUnite(getType(), TypeCarac.VISION));
    }
	
    /**
     * Fonction qui renvoie la modulation à apporter à l'attaque et la défense. Cela dépend du nombre de points d'expérience.
     * @return La valeur à ajouter à l'attaque et la défense dans les formules.
     */
    protected float modulationVisionDep() {
        switch (this.getNiveau()) {
        case 0:
            return 0;

        case 1:
            return (float) 0.1;

        case 2:
            return (float) 0.2;

        case 3:
            return (float) 0.3;

        default:
            return 0;
        }
    }

    /**
     * Initialise l'attribut chance d'une unité :
     * Nombre aléatoire entre 0 et 1, avec une espérance de 0.25
     */
    public float initChance() {
        this.chance = (float) (Math.random() * Math.random());
        return this.chance;
    }
	
    /**
     * Retourne le type du pokémon
     * @return type
     */
    @Override
    public abstract TypeUnite getType();
	
    public abstract int getXp();
	
    /**
     * Cette fonction gère un combat complet, conformément aux règles.
     * Elle ne s'applique qu'à un combat au corps à corps
     * @param attaque Unité à combattre.
     * @return boolean : vrai si l'unité this gagne le combat.
     */
    public boolean combatCorpsACorps(Unite attaque) {
        // this.getEquipe().getPartie().getJeuView().setVisible(true);
        int pertePVAtt = 0;
        int pertePVDef = 0;
        int gainXP = 0;
		
        if (attaque.getType() == TypeUnite.TELEPORTEUR) {
            this.getCaseTerrain().retirerUniteSansRetirerEquipe();
            Teleporteur tp = (Teleporteur) attaque;

            tp.getDestination().ajouterUnite(this);
            return false;
        }

        if (getRegles().getModeCombat() == TypeCombat.ASSAUT) {
            pertePVDef = this.lanceCoup(attaque);
            if (!attaque.mort() && attaque.peutRiposter()) {
                pertePVAtt = attaque.lanceCoup(this);
            }
        } else {
            if (getRegles().getModeCombat() == TypeCombat.MORT) {
				
                while (!this.mort() && !attaque.mort()) {
                    
                    pertePVDef = pertePVDef + this.lanceCoup(attaque);
                    if (attaque.peutRiposter() && !attaque.mort()) {
                        
                        pertePVAtt = pertePVAtt + attaque.lanceCoup(this);
                    }
                    
                }
            }
        }
		
        if (attaque.estObjet()) {
            switch (attaque.getType()) {
            case OBSTACLE:
                // ne rien faire.
                break;

            case POTION:
                this.getEquipe().gainPotion();
                break;

            default:
                assert false;
                break;
            }
        }
		
        if (attaque.mort() && !this.mort()) {
            attaque.getCaseTerrain().retirerUnite();
			
            this.getCaseTerrain().retirerUniteSansRetirerEquipe();
            attaque.getCaseTerrain().ajouterUnite(this);
			
            gainXP = this.gainXP(attaque);
			
            if (!attaque.estObjet()) {
                if (this.getEquipe().getType() == TypeEquipe.HUMAIN) {
                    JOptionPane.showMessageDialog(null,
                            "Votre unité a attaqué une unité ennemie.\nVotre unité a infligé "
                            + pertePVDef
                            + " points de dégâts.\nL'ennemi a infligé "
                            + pertePVAtt
                            + " points de dégâts.\nL'ennemi est mort. Votre unité a gagné "
                            + gainXP + " points d'XP.",
                            "Information",
                            JOptionPane.INFORMATION_MESSAGE);
                } else {
                    JOptionPane.showMessageDialog(null,
                            "Une unité ennemie a attaqué une de vos unités.\nElle a infligé "
                            + pertePVDef
                            + " points de dégâts.\nVotre unité a infligé "
                            + pertePVAtt
                            + " points de dégâts.\nVotre unité est morte. L'unité ennemie a gagné "
                            + gainXP + " points d'XP.",
                            "Information",
                            JOptionPane.INFORMATION_MESSAGE);
                }
            } else {
                if (this.getEquipe().getType() == TypeEquipe.HUMAIN) {
                    if (attaque.getType() == TypeUnite.POTION) {
                        JOptionPane.showMessageDialog(null,
                                "Votre unité a pris la potion trouvée sur le sol !",
                                "Information", JOptionPane.INFORMATION_MESSAGE);
                    } else if (attaque.getType() == TypeUnite.OBSTACLE) {
                        JOptionPane.showMessageDialog(null,
                                "Votre unité a détruit l'obstacle !",
                                "Information", JOptionPane.INFORMATION_MESSAGE);
                    }
                }
					
            }
			
            if (this.equipe.getPartie().finDePartie() != null) {
                new FinPartieView(this.equipe.getPartie(),
                        this.equipe.getPartie().finDePartie().getEnnemi());
            }
            return true;
        } else {
            if (this.mort()) {
				
                if (attaque.getType() != TypeUnite.DRESSEUR) {
                    Pokemon pok = (Pokemon) attaque;

                    gainXP = pok.gainXP(this);
                    if (this.getEquipe().getType() == TypeEquipe.HUMAIN) {
                        JOptionPane.showMessageDialog(null,
                                "Votre unité a attaqué une unité ennemie.\nVotre unité a infligé "
                                + pertePVDef
                                + " points de dégâts.\nL'ennemi a infligé "
                                + pertePVAtt
                                + " points de dégâts.\nVotre unité est morte. L'ennemi a gagné "
                                + gainXP + " XP.",
                                "Information",
                                JOptionPane.INFORMATION_MESSAGE);
                    } else {
                        JOptionPane.showMessageDialog(null,
                                "Une unité ennemie a attaqué une de vos unités.\nElle a infligé "
                                + pertePVDef
                                + " points de dégâts.\nVotre unité a infligé "
                                + pertePVAtt
                                + " points de dégâts.\nL'unité ennemie est morte. Votre unité a gagné "
                                + gainXP + " XP.",
                                "Information",
                                JOptionPane.INFORMATION_MESSAGE);
                    }
                } else {
                    if (this.getEquipe().getType() == TypeEquipe.HUMAIN) {
                        JOptionPane.showMessageDialog(null,
                                "Votre unité a attaqué le dresseur ennemi.\nVotre unité a infligé "
                                + pertePVDef
                                + " points de dégâts.\nLe dresseur ennemi a infligé "
                                + pertePVAtt
                                + " points de dégâts.\nVotre unité est morte.",
                                "Information",
                                JOptionPane.INFORMATION_MESSAGE);
                    } else {
                        JOptionPane.showMessageDialog(null,
                                "Une unité ennemie a attaqué votre dresseur.\nElle a infligé "
                                + pertePVDef
                                + " points de dégâts.\nVotre dresseur a infligé "
                                + pertePVAtt
                                + " points de dégâts.\nL'unité ennemie est morte.",
                                "Information",
                                JOptionPane.INFORMATION_MESSAGE);
                    }
                }
                this.getCaseTerrain().retirerUnite();
            } else {
                if (this.getEquipe().getType() == TypeEquipe.HUMAIN) {
                    JOptionPane.showMessageDialog(null,
                            "Votre unité a attaqué une unité ennemie.\nVotre unité a infligé "
                            + pertePVDef
                            + " points de dégâts.\nL'ennemi a infligé "
                            + pertePVAtt + " points de dégâts.",
                            "Information",
                            JOptionPane.INFORMATION_MESSAGE);
                } else {
                    JOptionPane.showMessageDialog(null,
                            "Une unité ennemie a attaqué une de vos unités.\nElle a infligé "
                            + pertePVDef
                            + " points de dégâts.\nVotre unité a infligé "
                            + pertePVAtt + " points de dégâts.",
                            "Information",
                            JOptionPane.INFORMATION_MESSAGE);
                }
            }
        }
		
        this.setDeplacement(0);
        if (this.equipe.getPartie().finDePartie() != null) {
            new FinPartieView(this.equipe.getPartie(),
                    attaque.equipe.getPartie().finDePartie().getEnnemi());
        }
        return false;
    }

    abstract protected int gainXP(Unite attaque);

    /**
     * Fonction qui renvoie la modulation à apporter à l'attaque et la défense. Cela dépend du nombre de points d'expérience.
     * @return La valeur à ajouter à l'attaque et la défense dans les formules.
     */
    protected float modulationAttDef() {
        int xp = this.getXp();

        switch (this.getNiveau()) {
        case 0:
            return (float) ((-1) * 0.0013333 * xp * xp + 0.13 * xp);

        case 1:
            return (float) (0.00033825 * xp * xp - 0.0356 * xp + 4.739);

        case 2:
            return (float) (0.0201 * xp + 3.96);

        case 3:
            return 10;

        default:
            return 0;
        }
    }

    /**
     * Accesseur de la capacité d'attaque.
     * @return Nombre de points d'attaque.
     */
    @Override
    public int getAttaque() {
        return (int) (modulationAttDef()
                + getRegles().getCaracUnite(this.getType(), TypeCarac.ATTAQUE));
    }

    /**
     * Accesseur de la capacité de défense.
     * @return Nombre de points de défense.
     */
    @Override
    public int getDefense() {
        return (int) (modulationAttDef()
                + getRegles().getCaracUnite(this.getType(), TypeCarac.DEFENSE));
    }
	
    @Override
    abstract public int getNiveau();
	
    /**
     * Réinitialise une unité au debut de chaque tour
     */
    public abstract void initTour();
	
    /**
     * Méthode appellée lors de l'utilisation d'une potion : ajout de PV.
     */
    public void utiliserPotion() {
        setPv(getRegles().getCaracUnite(this.getType(), TypeCarac.PV));
    }
	
    public String getName() {
        int niveau = this.getNiveau();

        switch (this.getType()) {
        case FEU: 
            if (niveau == 0) {
                return "Salameche";
            } else if (niveau == 1) {
                return "Reptincel";
            } else if (niveau >= 2) {
                return "Dracaufeu";
            }
            break;
		
        case PLANTE: 
            if (niveau == 0) {
                return "Bulbizarre";
            } else if (niveau == 1) {
                return "Herbizarre";
            } else if (niveau >= 2) {
                return "Florizarre";
            }
            break;
		
        case EAU:
            if (niveau == 0) {
                return "Carapuce";
            } else if (niveau == 1) {
                return "Carabaffe";
            } else if (niveau >= 2) {
                return "Tortank";
            }
            break;
			
        case ELECTRIQUE: 
            if (niveau == 0) {
                return "Pichu";
            } else if (niveau == 1) {
                return "Pikachu";
            } else if (niveau >= 2) {
                return "Raichu";
            }
            break;

        case VOL: 
            if (niveau == 0) {
                return "Roucool";
            } else if (niveau == 1) {
                return "Roucoups";
            } else if (niveau >= 2) {
                return "Roucarnage";
            }
            break;
        }
        return null;
    }
}
